package com.asset.dispoal.poi.documentation.service.commom;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.support.ExcelTypeEnum;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.asset.dispoal.poi.documentation.domain.db.FeatureTable;
import com.asset.dispoal.poi.documentation.domain.db.FeatureTableExpand;
import com.asset.dispoal.poi.documentation.domain.db.JudicialDocument;
import com.asset.dispoal.poi.documentation.domain.db.JudicialDocumentParam;
import com.asset.dispoal.poi.documentation.domain.db.mapper.DocumentationMapper;
import com.asset.dispoal.poi.documentation.domain.dto.AppointCaseDTO;
import com.asset.dispoal.poi.documentation.domain.dto.AppointCaseTemplateDTO;
import com.asset.dispoal.poi.documentation.domain.dto.AppointDTO;
import com.asset.dispoal.poi.documentation.domain.dto.IdCardLitigation;
import com.asset.dispoal.poi.documentation.dto.CaseDocumentationRequest;
import com.asset.dispoal.poi.documentation.service.commom.util.IdcardProUtil;
import com.deepoove.poi.XWPFTemplate;
import com.deepoove.poi.config.Configure;
import lombok.SneakyThrows;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.util.CellAddress;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cglib.beans.BeanMap;
import org.springframework.context.expression.MapAccessor;
import org.springframework.core.env.Environment;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.common.TemplateParserContext;
import org.springframework.expression.spel.standard.SpelExpression;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import tech.mhuang.pacebox.core.date.DateTimeUtil;
import tech.mhuang.pacebox.core.ddd.BaseCommand;
import tech.mhuang.pacebox.core.id.BaseIdeable;
import tech.mhuang.pacebox.core.io.IOUtil;
import tech.mhuang.pacebox.core.util.CollectionUtil;
import tech.mhuang.pacebox.core.util.ObjectUtil;
import tech.mhuang.pacebox.core.util.StringUtil;
import tech.mhuang.pacebox.springboot.protocol.Result;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

/**
 * @author qr
 * @date 2022/5/6$z
 */
@Component
public class DownloadCaseDocumentCommand implements BaseCommand<CaseDocumentationRequest, Result> {

    @Autowired
    DocumentationMapper documentationMapper;

    @Autowired
    private BaseIdeable<String> idWorker;

    @Autowired
    private Environment environment;

    @SneakyThrows
    @Override
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public Result execute(CaseDocumentationRequest request) {
        //逻辑第一步查询数据
        AppointDTO appointDTO = documentationMapper.getAppointDtoById(request.getAppointId());
        if (ObjectUtil.isEmpty(appointDTO)) {
            return Result.faild("委派数据不存在");
        }
        List<AppointCaseDTO> appointCaseDTOList = documentationMapper.queryCaseByAppointId(request.getAppointId());
        if (CollectionUtil.isEmpty(appointCaseDTOList)) {
            return Result.faild("委派的案件数据不存在");
        }
        //检测数据
        if ("2".equals(request.getMode())) {
            //找到对应的合同号和身份证号
            List<IdCardLitigation> idCardNoList = EasyExcel.read(request.getFile().getInputStream()).head(IdCardLitigation.class).sheet().doReadSync();
            if (CollectionUtil.isEmpty(idCardNoList)) {
                return Result.faild("下载的数据不存在");
            }
            appointCaseDTOList = appointCaseDTOList.stream().filter(appointCaseDTO -> {
                for (IdCardLitigation idCardLitigation : idCardNoList) {
                    if (StringUtil.isEmpty(idCardLitigation.getIdCard()) && StringUtil.equals(appointCaseDTO.getLitigationNumber(), idCardLitigation.getLitigationNumber())) {
                        return true;
                    }
                    if (StringUtil.isEmpty(idCardLitigation.getLitigationNumber()) && StringUtil.equals(appointCaseDTO.getBorrowerIdCard(), idCardLitigation.getIdCard())) {
                        return true;
                    }
                    if (StringUtil.equals(appointCaseDTO.getLitigationNumber(), idCardLitigation.getLitigationNumber()) && StringUtil.equals(appointCaseDTO.getBorrowerIdCard(), idCardLitigation.getIdCard())) {
                        return true;
                    }
                }
                return false;
            }).collect(Collectors.toList());
        }

        return encapsulation(appointCaseDTOList, request, appointDTO);
    }

    @SneakyThrows
    private Result encapsulation(List<AppointCaseDTO> appointCaseDTOList, CaseDocumentationRequest request, AppointDTO appointDTO) {
        List<Map<String, Object>> outList = new ArrayList<>();
        if ("1".equals(request.getFileType())) {
            //司法文书
            JudicialDocument document = documentationMapper.getJudicialDocumentById(request.getTemplateId());
            if (ObjectUtil.isEmpty(document)) {
                return Result.faild("文书或文件不存在");
            }
            Configure configure = Configure.builder().setValidErrorHandler(new Configure.DiscardHandler()).useSpringEL(false).build();
            List<JudicialDocumentParam> documentParamList = documentationMapper.queryJudicialDocumentParamByDocumentId(request.getTemplateId());

            Map<String, Object> map = new HashMap<>(2);
            byte[] templateByte = null;
            if (CollectionUtil.isNotEmpty(documentParamList)) {
                Map<String, Object> templateParamMap = new HashMap<>(documentParamList.size());
                for (JudicialDocumentParam param : documentParamList) {
                    templateParamMap.put(param.getParam(), param.getValue());
                }
                Configure templateConfigure = Configure.builder().setValidErrorHandler(new Configure.DiscardHandler()).build();
                ByteArrayOutputStream output = new ByteArrayOutputStream();
                XWPFTemplate.compile(environment.getProperty("file.evidence.url") + document.getFileUrl(), templateConfigure).render(templateParamMap).write(output);
                templateByte = output.toByteArray();
            }
            for (AppointCaseDTO appointCaseDTO : appointCaseDTOList) {
                AppointCaseTemplateDTO templateDTO = new AppointCaseTemplateDTO();
                templateDTO.addAppointDTO(appointDTO);
                templateDTO.addAppointCaseDTO(appointCaseDTO);
                templateDTO.setToday(DateTimeUtil.currentDate());
                String sex = "男";
                if (IdcardProUtil.getSexFromIdCard(appointCaseDTO.getBorrowerIdCard()) == 2) {
                    sex = "女";
                }
                templateDTO.setSex(sex);
                templateDTO.setBirthday(IdcardProUtil.getBirthDayFromIdCard(appointCaseDTO.getBorrowerIdCard()));
                String format = String.format("%s-%s.%s", appointCaseDTO.getBorrowerName(), appointCaseDTO.getBorrowerIdCard(), "docx");
                ByteArrayOutputStream output = new ByteArrayOutputStream();
                byte[] outByte;
                if (templateByte == null) {
                    XWPFTemplate.compile(environment.getProperty("file.evidence.url") + document.getFileUrl(), configure).render(templateDTO).write(output);
                } else {
                    XWPFTemplate.compile(new ByteArrayInputStream(templateByte), configure).render(templateDTO).write(output);
                }
                outByte = output.toByteArray();
                map.put("templateName", format);
                map.put("outByte", outByte);
                outList.add(map);
            }
        } else {
            //要素表
            FeatureTable featureTable = documentationMapper.getFeatureTableById(request.getTemplateId());
            if (ObjectUtil.isEmpty(featureTable)) {
                return Result.faild("要素表不存在");
            }
            List<FeatureTableExpand> featureTableExpandList = documentationMapper.queryFeatureTableExpandByTableId(request.getTemplateId());

            for (AppointCaseDTO appointCaseDTO : appointCaseDTOList) {
                AppointCaseTemplateDTO templateDTO = new AppointCaseTemplateDTO();
                templateDTO.addAppointDTO(appointDTO);
                templateDTO.addAppointCaseDTO(appointCaseDTO);
                templateDTO.setToday(DateTimeUtil.currentDate());
                String sex = "男";
                if (IdcardProUtil.getSexFromIdCard(appointCaseDTO.getBorrowerIdCard()) == 2) {
                    sex = "女";
                }
                templateDTO.setSex(sex);
                templateDTO.setBirthday(IdcardProUtil.getBirthDayFromIdCard(appointCaseDTO.getBorrowerIdCard()));
                BeanMap templateMap = BeanMap.create(templateDTO);
                ByteArrayOutputStream output = new ByteArrayOutputStream();
                try (ExcelWriter excelWriter = EasyExcel.write(output).withTemplate(environment.getProperty("file.evidence.url") + featureTable.getUrl()).excelType(ExcelTypeEnum.XLSX).build()) {
                    Workbook workbook = excelWriter.writeContext().writeWorkbookHolder().getCachedWorkbook();
                    if (featureTableExpandList != null && featureTableExpandList.size() > 0) {
                        //下拉或者ocr
                        for (FeatureTableExpand featureTableExpand : featureTableExpandList) {
                            String[] split = featureTableExpand.getSheetPage().split(",");
                            for (String s : split) {
                                Sheet sheet = workbook.getSheetAt(Integer.parseInt(s));
                                CellAddress address = new CellAddress(featureTableExpand.getLocation());
                                Row row = sheet.getRow(address.getRow());
                                //获取行
                                Cell cell = row.getCell(address.getColumn());
                                //获取列
                                cell.setCellValue((String) templateMap.get(featureTableExpand.getField()));
                            }
                        }
                    }
                    ExpressionParser parser = new SpelExpressionParser();
                    int numberSheet = workbook.getNumberOfSheets();
                    StandardEvaluationContext context = new StandardEvaluationContext();
                    MapAccessor propertyAccessor = new MapAccessor();
                    context.setVariables(BeanMap.create(templateDTO));
                    context.setPropertyAccessors(Collections.singletonList(propertyAccessor));
                    TemplateParserContext templateParserContext = new TemplateParserContext("{{", "}}");
                    for (int i = 0; i < numberSheet; i++) {
                        WriteSheet writeSheet = EasyExcel.writerSheet(i).build();
                        Sheet sheet = workbook.getSheetAt(i);
                        Iterator<Row> rowIterator = sheet.rowIterator();
                        while (rowIterator.hasNext()) {
                            Row row = rowIterator.next();
                            Iterator<Cell> cellIterator = row.cellIterator();
                            while (cellIterator.hasNext()) {
                                Cell cell = cellIterator.next();
                                try {
                                    String templateValue = cell.getStringCellValue();
                                    SpelExpression expression = (SpelExpression) parser.parseExpression(templateValue, templateParserContext);
                                    expression.setEvaluationContext(context);
                                    cell.setCellValue((String) expression.getValue(templateDTO));
                                } catch (Exception ignored) {

                                }
                            }
                        }
                        excelWriter.fill(templateMap, writeSheet);
                    }
                }
                Map<String, Object> map = new HashMap<>();
                byte[] outByte = output.toByteArray();
                map.put("templateName", String.format("%s-%s-%s.xlsx", templateDTO.getBorrowerName(), templateDTO.getBorrowerIdCard(), featureTable.getName()));
                map.put("outByte", outByte);
                outList.add(map);
            }
        }
        String id = idWorker.generateId();
        String generatorUrl = String.format("%s/%s.zip", environment.getProperty("file.generator.url"), id);
        return generatorFile(outList, "/" + generatorUrl);
    }

    private Result generatorFile(List<Map<String, Object>> outList, String generatorUrl) throws IOException {
        ZipOutputStream out = null;
        OutputStream os = null;
        try {
            File file1 = new File(environment.getProperty("file.evidence.url") + generatorUrl);
            if (!file1.exists()) {
                File fileParent = new File(file1.getParent());
                fileParent.mkdirs();
            }
            os = Files.newOutputStream(Paths.get(environment.getProperty("file.evidence.url") + generatorUrl));
            byte[] buffer = new byte[1024];
            //生成的ZIP文件名为Demo.zip
            out = new ZipOutputStream(os);
            for (Map<String, Object> map : outList) {
                InputStream fis = new ByteArrayInputStream((byte[]) map.get("outByte"));
                out.putNextEntry(new ZipEntry((String) map.get("templateName")));
                int len;
                //读入需要下载的文件的内容，打包到zip文件
                while ((len = fis.read(buffer)) != -1) {
                    out.write(buffer, 0, len);
                }
                out.flush();
                out.closeEntry();
                fis.close();
            }
        } finally {
            IOUtil.close(out, os);
        }

        return Result.ok(generatorUrl);
    }
}
